package com.geek.gkfood.biz.config;

import com.geek.gkfood.framework.dispatch.SpringCtxUtils;
import com.geek.gkfood.util.json.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.objenesis.instantiator.ObjectInstantiator;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <h1>配置变更事件监听器。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/9/24 17:54
 */
@Slf4j
@Component
public class UniversalCtrlConfigChangeListener {

    public static final String SPECAIL_PREFIX = "Universal_Ctrl_";

    public void onAdd(String key, String value){
        onUpdate(key, value);
    }

    public void onUpdate(String key, String value){
        onUpdateInner(key, value);
    }

    /**
     * <pre>
     *    [
     *        "classFuleName",
     *        "methodName",
     *        "params0_ClassFullName,params1_ClassFullName",
     *        "params[0] 参数对应的 json 字符串内容",
     *        "params[1] 参数对应的 json 字符串内容"
     *    ]
     * </pre>
     * @param key
     * @param value
     */
    private static void onUpdateInner(String key, String value) {
        if (key != null && !key.startsWith(SPECAIL_PREFIX)) {
            log.info(key + " 不是需要监听处理的键.");
            return;
        }

        try {
            List<String> args = JacksonUtils.fromJson(value, List.class, String.class);

            // 解析得到类对象
            Class<?> clz = Class.forName(args.get(0));

            // 解析得到方法入参类型对象
            String[] paramsTypeList = args.get(2).split(",", -1);
            int length = paramsTypeList.length;
            Class[] paramsClzTypeArray = getParamsClzTypeArray(paramsTypeList, length);

            // 解析得到方法参数列表
            Object[] paramsValueArray = getParamsValueArray(args, length, paramsClzTypeArray);

            // 反射得到方法对象
            Method method = clz.getDeclaredMethod(args.get(1), paramsClzTypeArray);
            ReflectionUtils.makeAccessible(method);

            // 尝试 3 种方式触发调用该方法逻辑
            tryInvokeCreateBean(clz, method, paramsValueArray);
            tryInvokeStaticClass(clz, method, paramsValueArray);
            tryInvokeSpringBean(clz, method, paramsValueArray);

            log.info("UniversalCtrlConfigChangeListener {} succ.", key);
        } catch (Exception e) {
            String errMsg = String.format("UniversalCtrlConfigChangeListener error. value=%s", value);
            log.error(errMsg, e);
        }
    }

    private static void tryInvokeCreateBean(Class<?> clz, Method method, Object[] paramsValueArray) {
        try {
            Objenesis objenesis = new ObjenesisStd();
            ObjectInstantiator objectInstantiator = objenesis.getInstantiatorOf(clz);
            Object instance = objectInstantiator.newInstance();
            method.invoke(instance, paramsValueArray);
        } catch (Exception e) {
            log.error("tryInvokeCreateBean error.", e);
        }
    }

    private static void tryInvokeStaticClass(Class<?> clz, Method method, Object[] paramsValueArray) {
        try {
            method.invoke(clz, paramsValueArray);
        } catch (Exception e) {
            log.error("tryInvokeStaticClass error.", e);
        }
    }

    private static void tryInvokeSpringBean(Class<?> clz, Method method, Object[] paramsValueArray) {
        try {
            Object bean = SpringCtxUtils.getBean(clz);
            method.invoke(bean, paramsValueArray);
        } catch (Exception e) {
            log.error("tryInvokeSpringBean error.", e);
        }
    }

    private static Object[] getParamsValueArray(List<String> args, int length, Class[] paramsClzTypeArray) {
        int idx2 = 0;
        Object[] paramsValueArray = new Object[length];
        while (idx2 < length) {
            String itemstr = args.get(3 + idx2);
            paramsValueArray[idx2] = parse2ParamsValue(itemstr, paramsClzTypeArray[idx2]);
            idx2 += 1;
        }
        return paramsValueArray;
    }

    private static Object parse2ParamsValue(String itemstr, Class clz) {
        if (String.class.isAssignableFrom(clz)) {
            return itemstr;
        }
        if (List.class.isAssignableFrom(clz)) {
            return JacksonUtils.fromJson(itemstr, List.class, Map.class);
        }
        if (Map.class.isAssignableFrom(clz)) {
            return JacksonUtils.fromJson(itemstr, Map.class);
        }
        return JacksonUtils.fromJson(itemstr, Map.class);
    }

    private static Class[] getParamsClzTypeArray(String[] paramsTypeList, int length) throws ClassNotFoundException {
        Class[] paramsClzTypeArray = new Class[length];
        int idx1 = 0;
        for (String type : paramsTypeList) {
            paramsClzTypeArray[idx1++] = Class.forName(type);
        }
        return paramsClzTypeArray;
    }

    public static void main(String[] args) {
        List valueList = new ArrayList();
        valueList.add("com.geek.gkfood.biz.jms.SnatchOrderMqListener");
        valueList.add("doRecvMsg");
        valueList.add("java.lang.String");
        valueList.add("{\"userId\":\"11\",\"orderId\":\"22\",\"orderJsonStr\":\"{}\"}");

        String key = "Universal_Ctrl_ABC";
        String value = JacksonUtils.toJson(valueList);
        new UniversalCtrlConfigChangeListener().onAdd(key, value);
    }
}